﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Objects.SqlClient;
using System.Dynamic;
using System.Linq;
using System.Text;
using EzDesk.Apps.Data.ProjectModule;
using EzDesk.Apps.Interface.ProjectModule;
using EzDesk.Apps.Model.ProjectModule;
using EzDesk.Apps.ServiceImpl.ProjectModule.History;
using EzDesk.Utility.ServiceStatusInfo;
using EzDesk.Utility.Json;
using EzDesk.Utility.Log;
using EzDesk.Utility.Extensions;
using EzDesk.Utility;
using EzDesk.Utility.Extensions;
using EzDesk.Core.Model;
using JdSoft.Apple.Apns.Notifications;

namespace EzDesk.Apps.ServiceImpl.ProjectModule
{
    public partial class ProjectService : IProjectService
    {
        #region [FeedManager 相关方法]
        //private string FeedTemplateStr_project_workitemTodo = "project_workitemTodo";

        private string FeedTemplateStr_project_workitemCreated = "project_workitemCreated";
        private string FeedTemplateStr_project_workitemExpired = "project_workitemExpired";
        private string FeedTemplateStr_project_projectUpdated = "project_projectUpdated";

        private Guid sendproject_workitemCreated(string memberstring, Workitem workitem)
        {
            var feedData = getWorkitemCreatedFeedData(memberstring, workitem);

            var feedId = _ezApiClient.SendFeed(
                 FeedTemplateStr_project_workitemCreated,
                 JsonHelper.ConvertToJson(feedData),
                 memberstring
                 );

            _ezApiClient.SetFeedRecord(workitem.Id, FeedTemplateStr_project_workitemCreated,
                                        workitem.OwnerId.ToString(), feedId, null);

            return feedId;
        }
        private Dictionary<string, string> getWorkitemCreatedFeedData(string memberstring, Workitem workitem)
        {
            var isFirst = true;
            var creatorName = _ezApiClient.GetSimpleUserById(workitem.CreatorId).Name;
            var titleContent = getTitleContent(workitem, ref isFirst, "{0} 创建了 ".FormatString(creatorName), "工作任务");
            var planOnContent = getPlanOnContent(workitem, ref isFirst);
            var confirmLink = getConfirmLink(workitem, ref isFirst);

            var feedData = new Dictionary<string, string>
                               {
                                   {"workitemId", workitem.Id.ToString()},
                                   {"titleContent", titleContent},
                                   {"planOnContent", planOnContent},
                                   {"confirmLink", confirmLink},                                   
                               };


            feedData.Merge(getWorkitemAdditionalFeedData(workitem, ref isFirst, false));

            return feedData;
        }




        private void sendproject_workitemExpired(string memberstring, Workitem workitem)
        {
            var feedData = getWorkitemExpired(workitem);

            _ezApiClient.SendFeed(
                 FeedTemplateStr_project_workitemExpired,
                 JsonHelper.ConvertToJson(feedData),
                 memberstring
                 );
        }

        private Dictionary<string, string> getWorkitemExpired(Workitem workitem)
        {
            var isFirst = true;
            var titleContent = getTitleContent(workitem, ref isFirst, null, "任务已经到期");
            var feedData = new Dictionary<string, string>
                               {
                                   {"workitemId", workitem.Id.ToString()},
                                   {"titleContent", titleContent},
                               };
            feedData.Merge(getWorkitemAdditionalFeedData(workitem, ref isFirst, false));
            return feedData;
        }


        private void removeWorkitemConfirmNotification(Guid workitemId, Guid ownerId)
        {
            var feedIdStr = _ezApiClient.GetFeedRecord(workitemId, FeedTemplateStr_project_workitemCreated,
                                                       ownerId.ToString());
            Guid feedId;
            if (Guid.TryParse(feedIdStr, out feedId))
            {
                _ezApiClient.SetFeedsIsAvailable(feedId.ToString(), ownerId.ToString(), false);
            }
        }



        private void sendproject_projectUpdated(Guid projectId, string updateContent)
        {
            var project =
                EzDeskProjectContainer.Go(container => container.ProjectSet.FirstOrDefault(p => p.Id == projectId));
            var projectTitle = project.Name;

            var feedData = new Dictionary<string, string>
                               {
                                   {"projectId", projectId.ToString()},
                                   {"projectTitle", projectTitle},
                                   {"updateContent", updateContent},
                               };

            var sendFeedId = _ezApiClient.GetFeedRecord(project.Id, FeedTemplateStr_project_projectUpdated, null);
            if (string.IsNullOrEmpty(sendFeedId))
            {
                // 新发一条 Feed
                var feedId = _ezApiClient.SendFeed(
                    FeedTemplateStr_project_projectUpdated,
                    JsonHelper.ConvertToJson(feedData),
                    project.MemberString
                    );
                _ezApiClient.SetFeedRecord(projectId, FeedTemplateStr_project_projectUpdated, project.MemberString,
                                           feedId, null);
            }
            else
            {
                var feedId = Guid.Parse(sendFeedId);
                // 之前已经发送过这条Feed
                // 更新这条Feed的接受者
                _ezApiClient.UpdateFeedRecipients(feedId, project.MemberString);
                _ezApiClient.SetFeedRecord(projectId, FeedTemplateStr_project_projectUpdated, project.MemberString, feedId, null);
                _ezApiClient.UpdateFeedsData(sendFeedId, JsonHelper.ConvertToJson(feedData));
            }
        }


        private void sendAddWorkitemToPersonFeeds(Workitem workitem)
        {
            // 给任务负责人发送一条确认的Notification
            if (workitem.OwnerId != workitem.CreatorId &&
                workitem.OwnerId.HasValue)
            {
                sendproject_workitemCreated(workitem.OwnerId.ToString(), workitem);
            }
        }


        private void sendWorkitemUpdateFeeds(Workitem oldWorkitem, Workitem newWorkitem, Guid actorId)
        {
            // 如果原来的那厮不是负责人了，remove掉他的feed
            if (oldWorkitem.OwnerId.HasValue && newWorkitem.OwnerId != oldWorkitem.OwnerId)
            {
                removeWorkitemConfirmNotification(oldWorkitem.Id, oldWorkitem.OwnerId.Value);
            }
            if (newWorkitem.OwnerId.HasValue && newWorkitem.OwnerId != oldWorkitem.OwnerId)
            {
                sendAddWorkitemToPersonFeeds(newWorkitem);
                if (newWorkitem.ProjectId.HasValue)
                {
                    var content = HistoryManager.CreateHistory(
                        new NewOwnerWorkitemHistoryRecorder(newWorkitem.Id, newWorkitem.OwnerId));
                    sendproject_projectUpdated(newWorkitem.ProjectId.Value, content);
                }
            }

            // 如果修改了任务内容
            if (oldWorkitem.Title != newWorkitem.Title ||
                oldWorkitem.ProjectId != newWorkitem.ProjectId ||
                oldWorkitem.TaskId != newWorkitem.TaskId ||
                oldWorkitem.PlanOn != newWorkitem.PlanOn ||
                oldWorkitem.Description != newWorkitem.Description ||
                oldWorkitem.Points != newWorkitem.Points
                )
            {
                // 修改了任务内容的项目历史
                if (newWorkitem.ProjectId.HasValue)
                {
                    var content = HistoryManager.CreateHistory(
                        new ContentUpdatedWokitemHistoryRecorder(oldWorkitem, newWorkitem, actorId));
                    sendproject_projectUpdated(newWorkitem.ProjectId.Value, content);
                }
                if (oldWorkitem.ProjectId.HasValue && oldWorkitem.ProjectId != newWorkitem.ProjectId)
                {
                    var removeFromProjectContent = HistoryManager.CreateHistory(
                        new RemoveFromProjectWorkitemHistoryRecorder(oldWorkitem.Id, actorId)
                        );
                    sendproject_projectUpdated(oldWorkitem.ProjectId.Value, removeFromProjectContent);
                }
            }

        }

        #region get content for feeds


        private Dictionary<string, string> getWorkitemAdditionalFeedData(
            Workitem workitem, ref bool isFirst,
            bool includeTraceContent = true,
            bool includeProjectContent = true,
            bool includePlanOnContent = true
            )
        {
            var traceContent = "";
            var projectContent = "";
            var planOnContent = "";

            if (workitem.IsTrace && includeTraceContent)
            {
                traceContent = getTraceContent(workitem, ref isFirst);
            }
            if (workitem.ProjectId != null && includeProjectContent)
            {
                projectContent = getProjectContent(workitem, ref isFirst);
            }
            if (workitem.PlanOn != null && includePlanOnContent)
            {
                planOnContent = getPlanOnContent(workitem, ref isFirst);
            }

            var feedData = new Dictionary<string, string>
            {
                {"traceContent", traceContent},
                {"projectContent", projectContent},
                {"planOnContent", planOnContent},
            };
            return feedData;
        }

        private string getNewLineSymbol(ref bool isFirst)
        {
            var content = "";
            if (isFirst)
            {
                isFirst = false;
            }
            else
            {
                content = "<br />";
            }
            return content;
        }

        private string getTitleContent(Workitem workitem, ref bool isFirst, string prefixStr = "", string suffixStr = "")
        {
            var titleContentFormatBuilder = new StringBuilder("\"{0}\"");
            if (!string.IsNullOrEmpty(prefixStr))
            {
                titleContentFormatBuilder.Insert(0, prefixStr + " ");
            }
            if (!string.IsNullOrEmpty(suffixStr))
            {
                titleContentFormatBuilder.Append(" " + suffixStr);
            }
            string titleContent = getNewLineSymbol(ref isFirst) +
                           string.Format(titleContentFormatBuilder.ToString(), workitem.Title);
            return titleContent;
        }

        private string getConfirmLink(Workitem workitem, ref bool isFirst)
        {
            return "<ez:text>收到</ez:text>";
        }


        private string getPlanOnContent(Workitem workitem, ref bool isFirst)
        {
            string planOnContent = "";
            if (workitem.PlanOn.HasValue)
            {
                planOnContent = getNewLineSymbol(ref isFirst) +
                                string.Format("在 {0} 到期", workitem.PlanOn.Value.ToFullDate());
            }
            return planOnContent;
        }

        private string getTraceContent(Workitem workitem, ref bool isFirst)
        {
            var creator = _ezApiClient.GetSimpleUserById(workitem.CreatorId);
            var traceContent = getNewLineSymbol(ref isFirst) + string.Format("{0} 正在追踪这个任务", creator.Name);

            return traceContent;
        }

        private string getProjectContent(Workitem workitem, ref bool isFirst)
        {
            var projectId = workitem.ProjectId.Value;

            Project project = null;
            Task task = null;
            EzDeskProjectContainer.Go(
                container =>
                {
                    project = container.ProjectSet.FirstOrDefault(p => p.Id == projectId);
                    if (workitem.TaskId != null)
                    {
                        task = container.TaskSet.FirstOrDefault(p => p.Id == workitem.TaskId.Value);
                    }
                });

            var projectContent = getNewLineSymbol(ref isFirst) +
                             string.Format("与 {0} 项目{1}关联", project.Name, task == null
                                                                              ? ""
                                                                              : string.Format("({0})", task.Name));
            return projectContent;
        }
        #endregion


        #endregion

        #region [notication for mobile]

        private int notification_add = 0;
        private int notification_confirm = 1;
        private int notification_finish = 2;
        private int notification_expire = 3;

        private static StatusInfo<Guid> addNotificationForAndorid(int type, Guid userId, string content)
        {
            return StatusInfo.Wrap(
             () =>
                 {
                     var notification = new NotificationForAndroid()
                                            {
                                                Id = Guid.NewGuid(),
                                                UserId = userId,
                                                Type = type,
                                                Content = content,
                                                IsReaded = false
                                            };
                 EzDeskProjectContainer.GoAndSave(
                     entity => entity.NotificationForAndroidSet.AddObject(notification));


                 return notification.Id;
             });
        }

        private void sendNotificationEntry(String notificationData, Guid receiverId, Guid workitemId)
        {
            //查看是否存在device_token
            var deviceToken = _ezApiClient.GetDeviceToken(receiverId);
            if (deviceToken == null || deviceToken.Equals("")) return;

            //发送notification
            var alertBody = notificationData;
            var args = new { workitemId };
            sendNotificantionToAppleApns(alertBody, deviceToken, args);
        }

        private void saveAddWorkitemNoti(Workitem workitem)
        {
            var creatorName = _ezApiClient.GetSimpleUserById(workitem.CreatorId).Name;
            var titleContent = workitem.Title;
            var notificationData = "收到 " + creatorName + " 分配的新任务 " + titleContent + "。";
            //ios
            sendNotificationEntry(notificationData, workitem.OwnerId.GetValueOrDefault(), workitem.Id);
            //android
            addNotificationForAndorid(notification_add, workitem.OwnerId.GetValueOrDefault(), notificationData);

        }



        private void saveWorkitemConfirmedNoti(Workitem workitem)
        {
            var ownerName = _ezApiClient.GetSimpleUserById(workitem.OwnerId.GetValueOrDefault()).Name;
            var titleContent = workitem.Title;
            var notificationData = ownerName + "确认收到 " + titleContent + " 任务。";

            sendNotificationEntry(notificationData, workitem.CreatorId, workitem.Id);
            addNotificationForAndorid(notification_confirm, workitem.CreatorId, notificationData);
        }


        private void saveWorikitemExpiredNotiForCreator(Workitem workitem)
        {
            var ownerName = _ezApiClient.GetSimpleUserById(workitem.OwnerId.GetValueOrDefault()).Name;
            var titleContent = workitem.Title;
            var notificationData = ownerName + "负责的 " + titleContent + " 任务过期了。";

            sendNotificationEntry(notificationData, workitem.CreatorId, workitem.Id);
            addNotificationForAndorid(notification_expire, workitem.CreatorId, notificationData);
        }
        private void saveWorikitemExpiredNotiForOwner(Workitem workitem)
        {
            var titleContent = workitem.Title;
            var notificationData = "你的 " + titleContent + " 任务过期了。";

            sendNotificationEntry(notificationData, workitem.OwnerId.GetValueOrDefault(), workitem.Id);
            addNotificationForAndorid(notification_expire, workitem.OwnerId.GetValueOrDefault(), notificationData);

        }

        private void saveWorikitemFinishedNoti(Workitem workitem)
        {
            var ownerName = _ezApiClient.GetSimpleUserById((Guid)workitem.OwnerId).Name;
            var titleContent = workitem.Title;
            var notificationData = ownerName + "完成了 " + titleContent + " 任务。";

            sendNotificationEntry(notificationData, workitem.CreatorId, workitem.Id);
            addNotificationForAndorid(notification_finish, workitem.CreatorId, notificationData);
        }

        //向apple apns 推送通知
        private void sendNotificantionToAppleApns(string alertBody, string deviceToken, dynamic args)
        {
            deviceToken = deviceToken.Replace("<", "").Replace(">", "").Replace(" ", "");

            var sandbox = true;
            var p12File = "apn_developer_identity.p12";
            var p12FilePassword = "123456";
            var p12Filename = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, p12File);

            var service = new NotificationService(sandbox, p12Filename, p12FilePassword, 1)
            {
                SendRetries = 5,
                ReconnectDelay = 5000
            };

            //test
            service.Error += service_Error;
            service.NotificationTooLong += service_NotificationTooLong;

            service.BadDeviceToken += service_BadDeviceToken;
            service.NotificationFailed += service_NotificationFailed;
            service.NotificationSuccess += service_NotificationSuccess;
            service.Connecting += service_Connecting;
            service.Connected += service_Connected;
            service.Disconnected += service_Disconnected;

            //notification
            var alertNotification = new Notification(deviceToken);

            alertNotification.Payload.Alert.Body = alertBody;
            alertNotification.Payload.AddCustom("workitemId", args.workitemId.ToString());
            
            alertNotification.Payload.Sound = "default";

            //Queue the notification to be sent
            service.QueueNotification(alertNotification);
            
        }

        #endregion


        #region [apns service callbacks]

        static void service_BadDeviceToken(object sender, BadDeviceTokenException ex)
        {
            Console.WriteLine("Bad Device Token: {0}", ex.Message);
        }

        static void service_Disconnected(object sender)
        {
            Console.WriteLine("Disconnected...");
        }

        static void service_Connected(object sender)
        {
            Console.WriteLine("Connected...");
        }

        static void service_Connecting(object sender)
        {
            Console.WriteLine("Connecting...");
        }

        static void service_NotificationTooLong(object sender, NotificationLengthException ex)
        {
            Console.WriteLine(string.Format("Notification Too Long: {0}", ex.Notification.ToString()));
        }

        static void service_NotificationSuccess(object sender, Notification notification)
        {
            Console.WriteLine(string.Format("Notification Success: {0}", notification.ToString()));
        }

        static void service_NotificationFailed(object sender, Notification notification)
        {
            Console.WriteLine(string.Format("Notification Failed: {0}", notification.ToString()));
        }

        static void service_Error(object sender, Exception ex)
        {
            Console.WriteLine(string.Format("Error: {0}", ex.Message));
        }

        #endregion

        #region [ private methods ]



        private Workitem getWorkitemById(Guid workitemId)
        {
            var item = EzDeskProjectContainer.Go(
                container =>
                {
                    var entity = container.WorkitemSet.FirstOrDefault(p => p.Id == workitemId);
                    container.LoadProperty(entity, e => e.Project);
                    return entity;
                });

            if (item == null)
            {
                throw new Exception("没有找到对应的工作任务");
            }
            return item;
        }

        private List<WorkitemStatistics> getWorkitemStatisticses(DateTime startDay, DateTime endDay, string memberstring)
        {

            var workitemCompletationStatusMapping = new Dictionary<Guid, bool>();

            return EzDeskProjectContainer.Go(
                db =>
                {
                    startDay = startDay.Date;
                    endDay = endDay.Date;

                    var userList =
                        _ezApiClient.GetAllUsersByMemberString(memberstring);
                    var userIdList = userList.Select(p => p.Id).ToList();
                    var finishedWorkitems =
                        db.WorkitemSet.Where(wi => wi.IsFinished 
                                                && userIdList.Contains(wi.OwnerId.Value) 
                                                && wi.FinishOn >= startDay 
                                                && wi.FinishOn < endDay)
                                       .ToList();
                    
                    return userList.Select(
                        user =>
                        {
                            var ownWorkitems = finishedWorkitems.Where(wi => wi.OwnerId == user.Id);
                            var totalCount = ownWorkitems.Count();
                            var onTimeFinishedCount = ownWorkitems.Where(wi => isOnTimeCompletation(db, wi)).Count();
                            var delaiedCount = ownWorkitems.Where(wi => wi.Status == WorkitemStatus.Expired).Count();

                            //var delaiedFinishedWorkitems = ownWorkitems.Where(wi => !isOnTimeCompletation(db, wi));
                            //var delaiedCount = delaiedWorkitems.Count();

                            return new WorkitemStatistics()
                                       {
                                           TargetUser = user,
                                           OnTimeCompletionRate = onTimeFinishedCount + " - " + totalCount,
                                           WoritemDelaiedCount = delaiedCount,
                                           TotalPoints = ownWorkitems.Sum(p => p.Points),
                                       };
                        })
                        .OrderByDescending(p => p.TotalPoints)
                        .ThenBy(p => p.TargetUser.Dept.DeptName)
                        .ToList()
                        .FillIndex();
                });
        }

        private bool isOnTimeCompletation(EzDeskProjectContainer db, Workitem workitem)
        {
            DateTime planOn;
            if (workitem.PlanOn.HasValue)
            {
                return workitem.PlanOn.Value >= workitem.FinishOn.Value;
            }

            db.LoadProperty(workitem, wi => wi.Task);
            if (workitem.Task != null)
            {
                return workitem.Task.GetEndDate() >= workitem.FinishOn.Value;
            }

            return true;
        }

        #endregion

        #region [ public methods ]

        public dynamic GetWorkitemDetailForMobile(Guid workitemId)
        {
            return EzDeskProjectContainer.Go(
                container =>
                    {
                        var workitem = container.WorkitemSet.Include("Project").Include("Task").Single(p => p.Id == workitemId);

                        SimpleUser owner = null;
                        if (workitem.OwnerId.HasValue)
                        {
                            owner = _ezApiClient.GetSimpleUserById(workitem.OwnerId.Value);
                        }
                        dynamic returnVal = new ExpandoObject();
                        returnVal.Workitem = workitem;
                        returnVal.Owner = owner;
                        Task rootTask = null;
                        Project project = null;
                        if (workitem.ProjectId.HasValue)
                        {
                            project = container.ProjectSet.Include("Task").FirstOrDefault(p => p.Id == workitem.ProjectId);
                            var tasks = project.Task;
                            var rootTaskIdList = project.RootTasks.Select(p => p.Id).ToList();
                            var theTask = workitem.Task;
                            
                            while (theTask != null && !rootTaskIdList.Contains(theTask.Id))
                            {
                                theTask = tasks.FirstOrDefault(p => p.Id == theTask.ParentTaskId);
                                if (theTask == null) break;
                            }
                            if (theTask != null)
                            {
                                rootTask = theTask;
                            }
                        }
                        returnVal.RootTask = rootTask;
                        returnVal.Project = project;
                        
                        return returnVal;
                    });
        }

        public StatusInfo UpdateWorkitemDescriptionForMobile(Guid workitemId, string description)
        {
            return StatusInfo.Wrap(
                () => EzDeskProjectContainer.GoAndSave(
                    container =>
                        {
                            // 更新备注
                            var workitem = container.WorkitemSet.First(p => p.Id == workitemId);
                            workitem.Description = description;
                        }));
        }

        public StatusInfo UpdateWorkitemTitleForMobile(Guid workitemId, string title)
        {
            return StatusInfo.Wrap(
                () => EzDeskProjectContainer.GoAndSave(
                    container =>
                    {
                        // 更新备注
                        var workitem = container.WorkitemSet.First(p => p.Id == workitemId);
                        workitem.Title = title;
                    }));
        }

        public StatusInfo UpdateWorkitemPlanOnForMobile(Guid workitemId, long planOn)
        {
            return StatusInfo.Wrap(
                () => EzDeskProjectContainer.GoAndSave(
                    container =>
                    {
                        // 更新备注
                        var workitem = container.WorkitemSet.First(p => p.Id == workitemId);
                        workitem.PlanOn = planOn.FromEpoch();
                    }));
        }

        public StatusInfo UpdateWorkitemOwnerForMobile(Guid workitemId, Guid? ownerId, Guid actorId)
        {
            var newWorkitem = EzDeskProjectContainer.Go(
                container =>
                    {
                        // 更新备注
                        var workitem = container.WorkitemSet.First(p => p.Id == workitemId);
                        workitem.OwnerId = ownerId;
                        return workitem;
                    });
            return UpdateWorkitem(newWorkitem, actorId);
        }

        public StatusInfo UpdateWorkitemProjectAndTaskForMobile(Guid? projectId, Guid? taskId, Guid? workitemId)
        {
            return StatusInfo.Wrap(
                () => EzDeskProjectContainer.GoAndSave(
                    container =>
                    {
                        // 更新备注
                        var workitem = container.WorkitemSet.First(p => p.Id == workitemId);
                        workitem.ProjectId = projectId;
                        workitem.TaskId = taskId;
                    }));
        }

        public void TestApns(string deviceToken, string body, string workitemId)
        {
            var args = new { workitemId };
            sendNotificantionToAppleApns(body, deviceToken, args);
        }

        public StatusInfo<List<NotificationForAndroid>> getFeedsForAndroid(Guid userId)
        {
            return StatusInfo.Wrap(
                () => EzDeskProjectContainer
                          .GoAndSave(db => db.NotificationForAndroidSet
                                               .Where(p => p.UserId == userId && !p.IsReaded.Value)
                                               .ToList()));
        }

        public StatusInfo cancelFeeds(List<Guid> idList)
        {
            return StatusInfo.Wrap(
               () => EzDeskProjectContainer.GoAndSave(container =>
               {
                   var feeds =
                       container.NotificationForAndroidSet.Where(f=> idList.Contains(f.Id));
                   foreach (var feed in feeds)
                   {
                       feed.IsReaded = true;
                   }
                   container.SaveChanges();


               }));
        }


        public StatusInfo<Guid> AddWorkitem(Workitem workitem)
        {
            return StatusInfo.Wrap(
                () => EzDeskProjectContainer.GoAndSave(
                    container =>
                    {
                        {
                            var newWorkitemId = Guid.NewGuid();
                            workitem.Id = newWorkitemId;
                            workitem.CreateOn = DateTime.Now;
                            workitem.UpdateOn = workitem.CreateOn;
                            workitem.Description = "";
                            workitem.IsAvailable = true;


                            if (workitem.OwnerId == null)
                            {
                                workitem.Status = WorkitemStatus.Ready;
                            }
                            else
                            {
                                workitem.Status = workitem.IsTrace && workitem.OwnerId != workitem.CreatorId
                                                      ? WorkitemStatus.WaitingForConfirm
                                                      : WorkitemStatus.Ongoing;
                            }

                            container.WorkitemSet.AddObject(workitem);
                            container.SaveChanges();


                            //为任务负责人 mobile notification
                            if (workitem.OwnerId != null && workitem.CreatorId != workitem.OwnerId)
                            {
                               saveAddWorkitemNoti(workitem);
                            }

                            sendAddWorkitemToPersonFeeds(workitem);
                            // 如果任务跟项目有关，那么给项目成员发送一条Feed)
                            if (workitem.ProjectId.HasValue)
                            {
                                var content = HistoryManager.CreateHistory(
                                    new CreateWorkitemHistoryRecorder(workitem.Id,
                                                                      workitem.CreatorId));
                                sendproject_projectUpdated(workitem.ProjectId.Value, content);
                            }

                            return newWorkitemId;
                        }
                    })
                );
        }

        public StatusInfo<Guid> AddFinishedWorkitem(Workitem workitem)
        {
            return StatusInfo.Wrap(
                () =>
                {
                    var newWorkitemId = Guid.NewGuid();
                    workitem.Id = newWorkitemId;
                    workitem.IsFinished = true;
                    workitem.CreateOn = DateTime.Now;
                    workitem.UpdateOn = workitem.CreateOn;
                    workitem.Status = WorkitemStatus.Completed;
                    workitem.IsAvailable = true;

                    EzDeskProjectContainer.GoAndSave(
                        entity => entity.WorkitemSet.AddObject(workitem));

                    if (workitem.ProjectId.HasValue)
                    {
                        var content = HistoryManager.CreateHistory(
                            new CreateWorkitemHistoryRecorder(workitem.Id,
                                                              workitem.CreatorId));
                        sendproject_projectUpdated(workitem.ProjectId.Value, content);
                    }
                    return newWorkitemId;
                });
        }

        public StatusInfo ConfirmWorkItem(Guid workitemId, Guid actorId)
        {
            return StatusInfo.Wrap(
                () => EzDeskProjectContainer.GoAndSave(container =>
                {
                    var workitem =
                       container.WorkitemSet.FirstOrDefault(w => w.Id == workitemId);

                    if (workitem.OwnerId != actorId)
                        throw new Exception("只有任务的负责人才能确认收到任务!");

                    workitem.StatusValue = (int)WorkitemStatus.Ongoing;
                    workitem.UpdateOn = DateTime.Now;

                    container.SaveChanges();

                    // 修改 负责人收到的那条Notification
                    removeWorkitemConfirmNotification(workitem.Id, workitem.OwnerId.Value);

                    // 发送手机端 推送
                    if (workitem.OwnerId != workitem.CreatorId)
                    {
                        saveWorkitemConfirmedNoti(workitem);
                    }

                }));

        }

        public StatusInfo CancelWorkItem(Guid workitemId, Guid actorId)
        {
            return StatusInfo.Wrap(
                () => EzDeskProjectContainer.GoAndSave(container =>
                {
                    var workitem =
                        container.WorkitemSet.FirstOrDefault(w => w.Id == workitemId);
                    workitem.IsAvailable = false;
                    container.SaveChanges();


                }));
        }

        public StatusInfo FinishWorkitem(Guid workitemId, Guid actorId, int estimatePoint)
        {
            return StatusInfo.Wrap(
                () =>
                EzDeskProjectContainer.GoAndSave(
                    container =>
                    {
                        var workitem =
                            getWorkitemById(workitemId);
                        if (workitem.IsFinished)
                        {
                            throw new Exception("这个任务已经被完成了");
                        }
                        if (workitem.OwnerId == null)
                        {
                            workitem.OwnerId = actorId;
                        }
                        workitem.FinishOn = DateTime.Now;
                        workitem.IsFinished = true;
                        workitem.StatusValue =
                            (int)WorkitemStatus.Completed;
                        workitem.Points = estimatePoint;
                        workitem.UpdateOn =
                            workitem.FinishOn;


                        container.WorkitemSet.Attach(workitem);
                        container.ObjectStateManager.ChangeObjectState(workitem, EntityState.Modified);

                        if (workitem.ProjectId.HasValue)
                        {
                            var content = HistoryManager.CreateHistory(
                                new FinishWorkitemHistoryRecorder(workitem.Id, actorId));
                            sendproject_projectUpdated(workitem.ProjectId.Value, content);
                        }
                        
                        // 任务完成时 发送手机推送
                        if (workitem.OwnerId != workitem.CreatorId)
                        {
                            saveWorikitemFinishedNoti(workitem);
                        }


                    })
                );
        }

        public StatusInfo PostponeWorkitem(Guid workitemId, DateTime newPlanOn, Guid actorId)
        {
            return StatusInfo.Wrap(
                   () => EzDeskProjectContainer.GoAndSave(
                       container =>
                       {
                           var workitem = getWorkitemById(workitemId);

                           workitem.PlanOn = newPlanOn;
                           workitem.StatusValue = (int)WorkitemStatus.Ongoing;

                           container.WorkitemSet.Attach(
                               workitem);
                           container.ObjectStateManager.ChangeObjectState(workitem, EntityState.Modified);


                           container.SaveChanges();

                           if (workitem.ProjectId.HasValue)
                           {
                               var content = HistoryManager.CreateHistory(
                                   new PostponeWorkitemHistoryRecorder(workitemId, actorId));
                               sendproject_projectUpdated(workitem.ProjectId.Value, content);
                           }
                       }));

        }
        public StatusInfo UpdateWorkitem(Workitem newWorkitem, Guid actorId)
        {
            return StatusInfo.Wrap(
                () => EzDeskProjectContainer.Go(
                    container =>
                    {
                        var workitem = container.WorkitemSet.First(p => p.Id == newWorkitem.Id);
                        var oldWorkitem = workitem.Clone();

                        var status = oldWorkitem.Status;
                        // 如果原来过期现在更改了过期时间
                        if (status == WorkitemStatus.Expired && newWorkitem.PlanOn > DateTime.Now.Date)
                        {
                            status = oldWorkitem.OwnerId.HasValue
                                         ? WorkitemStatus.Ongoing
                                         : WorkitemStatus.Ready;
                        }
                        // 如果修改了负责人，并且现在有负责人
                        if (newWorkitem.OwnerId.HasValue && newWorkitem.OwnerId != oldWorkitem.OwnerId)
                        {
                            status = newWorkitem.OwnerId != workitem.CreatorId
                                         ? WorkitemStatus.WaitingForConfirm
                                         : WorkitemStatus.Ongoing;
                        }
                        
                        // 生成新的workitem
                        workitem.Points = newWorkitem.Points;
                        workitem.PlanOn = newWorkitem.PlanOn;
                        workitem.OwnerId = newWorkitem.OwnerId;
                        workitem.ProjectId = newWorkitem.ProjectId;
                        workitem.Description = newWorkitem.Description;
                        workitem.Title = newWorkitem.Title;
                        workitem.TaskId = newWorkitem.TaskId;
                        workitem.IsTrace = newWorkitem.IsTrace;
                        workitem.Status = status;

                        // 如果以前没有负责人 现在重新分配了负责人
                        // 并且不是创建者的自己的任务
                        // 手机端发送一条推送
                        if (!oldWorkitem.OwnerId.HasValue && workitem.OwnerId.HasValue 
                            && workitem.CreatorId != workitem.OwnerId)
                        {
                            saveAddWorkitemNoti(workitem);
                        }


                        container.SaveChanges();

                        EzDeskAppApiHelper.DelaySendUpdateFeeds(oldWorkitem, workitem, actorId,
                                                                p => p.ActorId == actorId && p.OldObject.Id == workitem.Id,
                                                                sendWorkitemUpdateFeeds);
                    }
                    )
                );
        }


        // todo: 完善这个功能
        public StatusInfo SendDailyWorkBlog()
        {
            var today = DateTime.Today;
            return StatusInfo.Wrap(
            () => EzDeskProjectContainer.Go(container =>
            {
                var workitemCompletedToday =
                    container.WorkitemSet.Where(
                        workitem => workitem.FinishOn > today && workitem.FinishOn < today.AddDays(1));
                var dailyWorks = container.DailyWorksSet.Where(
                    dailyWork =>
                    dailyWork.Day >= today && dailyWork.Day < today.AddDays(1)
                    );

            }));
        }

        public StatusInfo CheckTimeoutWorkItems()
        {
            return StatusInfo.Wrap(
            () => EzDeskProjectContainer.Go(container =>
            {
                var now = DateTime.Now.Date;
                var workitemList = container.WorkitemSet.Where(w => w.PlanOn < now && !w.IsFinished && w.IsAvailable).ToList();

                foreach (var workitem in workitemList)
                {
                    workitem.Status = WorkitemStatus.Expired;
                    //通知这个任务的创建者 mobile notification
                    saveWorikitemExpiredNotiForCreator(workitem);

                    // 通知这个任务的负责人feed
                    // 通知这个任务的负责人 mobile notification
                    if (workitem.OwnerId.HasValue)
                    {
                        sendproject_workitemExpired(workitem.OwnerId.ToString(), workitem);
                        saveWorikitemExpiredNotiForOwner(workitem); 
                    }

                    // 任务过期的项目历史
                    if (workitem.ProjectId.HasValue)
                    {
                        var content = HistoryManager.CreateHistory(
                            new ExpiredWorkitemHistoryRecorder(workitem.Id));
                        sendproject_projectUpdated(workitem.ProjectId.Value, content);
                    }
                }
                container.SaveChanges();


            }));
        }

        public void CheckStatisticsReport()
        {
            if (DateTime.Now.Date == Convert.ToDateTime(DateTime.Now.AddMonths(1).ToString("yyyy-MM-01")).AddDays(-1))
            {
                //todo: 每月底发送当月工作评估的FEED
                //sendproject_statistics();
            }
        }



        public StatusInfo<List<Workitem>> GetWorkitemsInProjects(List<Guid> projectList)
        {
            return StatusInfo.Wrap(
                       () => EzDeskProjectContainer
                           .GoAndSave(db => db.WorkitemSet
                               .Where(p => projectList.Contains(p.ProjectId.Value) && p.IsAvailable)
                               .ToList()));

        }

        #region [ query methods ]
        public StatusInfo<List<Workitem>> GetWorkitemsWithoutOwner(Guid userId)
        {
            return StatusInfo.Wrap(
            () => EzDeskProjectContainer.Go(
            container =>
            {
                var userProjectIds
                    = container.MemberInProjectSet.Where(p => p.UserId == userId).Select(p => p.ProjectId).ToList();

                var workitemList = container.WorkitemSet.Include("Project").Include("Task")
                    .Where(w => w.OwnerId == null)
                    .Where(w => w.ProjectId == null || userProjectIds.Contains(w.ProjectId))
                    .ToList();
                return workitemList;
            }));
        }

        public StatusInfo<List<Workitem>> GetWorkitems(
            Guid? ownerId, Guid? creatorId, Guid? projectId, Guid? taskId,
            WorkitemStatus? status,
            bool? isFinished,
            DateTime? startCreateOn, DateTime? endCreateOn,
            DateTime? startFinishOn, DateTime? endFinishOn,
            int? maxCount,
            bool isAvailable = true)
        {
            return StatusInfo.Wrap(
                () =>
                {
                    var workitems = EzDeskProjectContainer.Go(
                        container =>
                        {
                            IQueryable<Workitem> query = container.WorkitemSet.Include("Project").Include("Task").Where(p=>p.IsAvailable == isAvailable);
                            if (ownerId.HasValue)
                            {
                                query = query.Where(p => p.OwnerId == ownerId.Value);
                            }
                            if (creatorId.HasValue)
                            {
                                query = query.Where(p => p.CreatorId == creatorId.Value);
                            }
                            if (projectId.HasValue)
                            {
                                query = query.Where(p => p.ProjectId == projectId.Value);
                            }
                            if (taskId.HasValue)
                            {
                                query = query.Where(p => p.TaskId == taskId.Value);
                            }
                            if (isFinished.HasValue)
                            {
                                query = query.Where(p => p.IsFinished == isFinished.Value);
                            }

                            if (startCreateOn.HasValue)
                            {
                                query = query.Where(p => p.CreateOn >= startCreateOn.Value);
                            }
                            if (endCreateOn.HasValue)
                            {
                                query = query.Where(p => p.CreateOn <= endCreateOn.Value);
                            }
                            if (startFinishOn.HasValue)
                            {
                                query = query.Where(p => p.FinishOn >= startFinishOn.Value);
                            }
                            if (endFinishOn.HasValue)
                            {
                                query = query.Where(p => p.FinishOn <= endFinishOn.Value);
                            }
                            if (status.HasValue)
                            {
                                query = query.Where(p => p.StatusValue == (int)status);
                            }
                            if (maxCount.HasValue)
                            {
                                query = query.Take(maxCount.Value);
                            }

                            return query.ToList();
                           
                        });
                    return workitems;
                });
        }

        public StatusInfo<Workitem> GetWorkitem(Guid workitemId)
        {
            return StatusInfo.Wrap(
                () =>
                {
                    var item = getWorkitemById(workitemId);
                    return item;
                });
        }


        public StatusInfo<List<WorkitemStatistics>> GetWorkitemStatistics(string memberstring, DateTime startDay, DateTime endDay)
        {
            return StatusInfo.Wrap(
                () => getWorkitemStatisticses(startDay, endDay, memberstring)
                );
        }



        #endregion

        #endregion


    }
}

